package repomanagement.controller;

    import java.sql.*;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import java.util.ArrayList;
    import repomanagement.entities.*;

/**
 *
 * @author Nicolas Drosos
 * Κλάση DBManager για την επικοινωνία με τη βάση δεδομένων
 */
public class DBManager {
    private String database = "repositoryDB";
    private String user = "app";
    private String password = "app";
    private String driver = "org.apache.derby.jdbc.ClientDriver";
    //private String connString = "jdbc:derby:";
    private String connString = "jdbc:derby://localhost:1527/";
    private Connection dBconnection;
    private Statement statement;

    // Ερωτήματα διαχείρισης προσωπικού (εισαγωγή, ενημέρωση, ανάκτηση, αφαίρεση)
    private PreparedStatement insertPerson;
    private PreparedStatement updatePerson;
    private PreparedStatement selectPersons;
    private PreparedStatement removePerson;

    // Ερωτήματα χρέωσης ξεχρέωσης υπολογιστή σε/από προσωπικό
    private PreparedStatement selectAssignedComputers;
    private PreparedStatement selectAvailableComputers;
    private PreparedStatement assignComputerToPerson;
    private PreparedStatement releaseComputerFromPerson;
    private PreparedStatement releaseComputerFromDeletedPerson;

    /**
     * Δημιουργός της DBManager. Αρχικοποιεί την επικοινωνία με τη βάση δεδομένων και
     * προτετοιμάζει τα απαιτούμενα ερωτήματα.
     */
    public DBManager(){
        try {
            // Φόρτωση της κλάσης του οδηγού της βάσης δεδομένων
           Class.forName(driver);
            // Σύνδεση με τη βάση δεδομένων
            dBconnection = DriverManager.getConnection(connString + database, user, password);
            statement = dBconnection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);

            // Επιλογή όλου του προσωπικού από τη βάση
            selectPersons = dBconnection.prepareStatement("SELECT * FROM APP.PERSON");
            // Ενημέρωση στοιχείων προσωπικού
            updatePerson = dBconnection.prepareStatement("UPDATE APP.PERSON SET NAME=?, ADDRESS=?, MAIL=? WHERE PERS_ID=?");
            // Διαγραφή προσωπικού
            removePerson = dBconnection.prepareStatement("DELETE FROM APP.PERSON WHERE PERS_ID=?");

            // Επιλογή υπολογιστών που έχουν χρεωθεί σε συγκεκριμένο άτομο
            selectAssignedComputers = dBconnection.prepareStatement("SELECT * FROM APP.COMPUTER WHERE PERS_ID=?");

            // Επιλογή διαθέσιμων υπολογιστών
            selectAvailableComputers = dBconnection.prepareStatement("SELECT * FROM APP.COMPUTER WHERE PERS_ID IS NULL");

            // Χρέωση υπολογιστή σε συγκεκριμένο άτομο
            assignComputerToPerson = dBconnection.prepareStatement("UPDATE APP.COMPUTER SET PERS_ID=? WHERE COMP_ID=?");
            // Ξεχρέωση υπολογιστή από άτομο
            releaseComputerFromPerson = dBconnection.prepareStatement("UPDATE APP.COMPUTER SET PERS_ID=NULL WHERE COMP_ID=?");
            // Ξεχρέωση υπολογιστή από διεγραμμένο προσωπικό
            releaseComputerFromDeletedPerson = dBconnection.prepareStatement("UPDATE APP.COMPUTER SET PERS_ID=NULL WHERE PERS_ID=?");

        } catch (Exception exc) {
            System.out.println(exc);
        }
    }

    /**
     * Χρεώνει τον υπολογιστή c στο άτομο p.
     * @param computer Το αντικείμενο υπολογιστή που θα ανατεθεί
     * @param p Το άτομο στο οποίο θα γίνει η χρέωση
     */
    public void assignComputerToPerson(Computer computer, Person p){
        try {
            assignComputerToPerson.setInt(1, p.getPersId());
            assignComputerToPerson.setInt(2, computer.getCompId());
            assignComputerToPerson.executeUpdate();
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Ξεχρέωση του υπολογιστή computer από την τρέχουσα χρέωσή του.
     * @param computer Ο υπολογιστής που θα ξεχρεωθεί
     */
    public void releaseComputerFromPerson(Computer computer){
        try {
            releaseComputerFromPerson.setInt(1, computer.getCompId());
            releaseComputerFromPerson.executeUpdate();
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Ανάκτηση των διαθέσιμων υπολογιστών, δηλαδή αυτών που δεν έχουν ακόμα χρεωθεί
     * @return Επιστρέφει μια λίστα με τους διαθέσιμους υπολογιστές
     */
    public ArrayList retrieveAvailableComputers(){
        try {
            ResultSet rs = selectAvailableComputers.executeQuery();
            ArrayList<Computer> availableComputers = new ArrayList<Computer>();
            while (rs.next()){
                Computer c = new Computer();
                c.setCompId(rs.getInt("COMP_ID"));
                c.setLabel(rs.getString("LABEL"));
                c.setSn(rs.getString("SN"));
                c.setModel(rs.getString("MODEL"));
                c.setCpu(rs.getString("CPU"));
                c.setRam(rs.getString("RAM"));                
                c.setDisk(rs.getString("DISK"));
                c.setType(rs.getInt("COMP_TYPE"));
                availableComputers.add(c);
            }
            return availableComputers;
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    /**
     * Ανάκτηση των χρεωμένων υπολογιστών για ένα συγκεκριμένο άτομο
     * @param selectedPerson Το άτομο του οποίου θέλουμε να λάβουμε τις χρεώσεις
     * @return Λίστα με τους υπολογιστές που είναι χρεωμένοι στο άτομο selectedPerson
     */
    public ArrayList retrieveAssignedComputers(Person selectedPerson){
        try {
            if (selectedPerson == null)
                return null;
            selectAssignedComputers.setInt(1, selectedPerson.getPersId());
            ResultSet rs = selectAssignedComputers.executeQuery();
            ArrayList<Computer> assignedComputers = new ArrayList<Computer>();
            while (rs.next()){
                Computer c = new Computer();
                c.setCompId(rs.getInt("COMP_ID"));
                c.setLabel(rs.getString("LABEL"));
                c.setSn(rs.getString("SN"));
                c.setModel(rs.getString("MODEL"));
                c.setCpu(rs.getString("CPU"));
                c.setRam(rs.getString("RAM"));                
                c.setDisk(rs.getString("DISK"));
                c.setType(rs.getInt("COMP_TYPE"));
                assignedComputers.add(c);
            }
            return assignedComputers;
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    /**
     * Ανάκτηση όλου του προσωπικού
     * @return Λίστα με αντικείμενα Person
     */
    public ArrayList retrievePersons(){
        try {
            ResultSet rs = selectPersons.executeQuery();
            ArrayList<Person> availablePersons = new ArrayList<Person>();
            while (rs.next()){
                Person p = new Person();
                p.setPersId(rs.getInt("PERS_ID"));
                p.setName(rs.getString("NAME"));
                p.setAddress(rs.getString("ADDRESS"));
                p.setMail(rs.getString("MAIL"));
                availablePersons.add(p);
            }
            return availablePersons;
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    /**
     * Αποθήκευση στη βάση ενός ατόμου
     * @param pers Το άτομο που θα αποθηκευτεί
     * @return Το ID του ατόμου που καταχωρήθηκε ή -1 αν η καταχώρηση δεν πραγματοποιήθηκε
     */
    public int storePerson(Person pers){
        try {
            int[] autoCol=new int[1];
            autoCol[0]=1;
            insertPerson = dBconnection.prepareStatement("INSERT INTO APP.PERSON (NAME, ADDRESS, MAIL) VALUES (?, ?, ?)", autoCol);

            insertPerson.setString(1, pers.getName());
            insertPerson.setString(2, pers.getAddress());
            insertPerson.setString(3, pers.getMail());
            insertPerson.executeUpdate();
            ResultSet rs = insertPerson.getGeneratedKeys();
            if (rs != null) {
                rs.next();
                int storedID = rs.getInt(1);
                return storedID;
            }
            return -1;
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
            return -1;
        }
    }

    
    /**
     * Ενημέρωση των στοιχείων ενός ατόμου
     * @param pers Το αντικείμενο που περιέχει τα νέα στοιχεία του ατόμου, το οποίο θα ενημερώσει
     * την αντίστοιχη εγγραφή στη βάση με το ίδιο ID.
     */
    public void updatePerson(Person pers){
        try {
        updatePerson.setString(1, pers.getName());
        updatePerson.setString(2, pers.getAddress());
        updatePerson.setString(3, pers.getMail());
        updatePerson.setInt(4, pers.getPersId());
        updatePerson.executeUpdate();
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Διαγραφή προσωπικού από τη βάση
     * @param pers Το άτομο που θα διαγραφεί
     */
    public void removePerson(Person pers){
        try {
        releaseComputerFromDeletedPerson.setInt(1, pers.getPersId());
        removePerson.setInt(1, pers.getPersId());
        releaseComputerFromDeletedPerson.executeUpdate();
        removePerson.executeUpdate();
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void initializeDB(){
        try {
            PreparedStatement deletePersons = dBconnection.prepareStatement("DELETE FROM APP.PERSON");
            PreparedStatement deleteComputers = dBconnection.prepareStatement("DELETE FROM APP.COMPUTER");
            deleteComputers.executeUpdate();
            deletePersons.executeUpdate();
            

            PreparedStatement addComputer1 = dBconnection.prepareStatement("INSERT INTO APP.COMPUTER (LABEL, SN, MODEL, CPU, RAM, COMP_TYPE, DISK) VALUES ('LAP_01', '23AS543W21', 'HP 2331', '3,2 GHz', '4 GB', 1, '500 GB')");
            PreparedStatement addComputer2 = dBconnection.prepareStatement("INSERT INTO APP.COMPUTER (LABEL, SN, MODEL, CPU, RAM, COMP_TYPE, DISK) VALUES ('PC_01', 'AA3235334345421', 'TURBO-X GAMERS', '2,6 GHz', '3 GB', 0, '250 GB')");
            PreparedStatement addComputer3 = dBconnection.prepareStatement("INSERT INTO APP.COMPUTER (LABEL, SN, MODEL, CPU, RAM, COMP_TYPE, DISK) VALUES ('PC_02', '22RTYWEC33W51', 'ALTEC-PC', '3,5 GHz', '1,5 GB', 0, '150 GB')");
            PreparedStatement addComputer4 = dBconnection.prepareStatement("INSERT INTO APP.COMPUTER (LABEL, SN, MODEL, CPU, RAM, COMP_TYPE, DISK) VALUES ('LAP_02', '66FDFW345GFD3', 'ACER 112', '4 GHz', '4 GB', 1, '250 GB')");
            PreparedStatement addComputer5 = dBconnection.prepareStatement("INSERT INTO APP.COMPUTER (LABEL, SN, MODEL, CPU, RAM, COMP_TYPE, DISK) VALUES ('LAP_03', 'GBF67U65HBT45', 'TOSHIBA SATELLITE 2', '4,2 GHz', '5 GB', 1, '800 GB')");
            addComputer1.executeUpdate();
            addComputer2.executeUpdate();
            addComputer3.executeUpdate();
            addComputer4.executeUpdate();
            addComputer5.executeUpdate();

            PreparedStatement addPerson1 = dBconnection.prepareStatement("INSERT INTO APP.PERSON (NAME, ADDRESS, MAIL) VALUES ('Δρόσος Νίκος', 'Ψηλορείτη 3, Γαλάτσι', 'ndrossos@cti.gr')");
            PreparedStatement addPerson2 = dBconnection.prepareStatement("INSERT INTO APP.PERSON (NAME, ADDRESS, MAIL) VALUES ('Γεωργίου Βασίλης', 'Μητροπόλεως 25, ΑΘήνα', 'vasilis@yahoo.com')");
            PreparedStatement addPerson3 = dBconnection.prepareStatement("INSERT INTO APP.PERSON (NAME, ADDRESS, MAIL) VALUES ('Αλεξίου Αντώνης', 'Πίνδου 33, Παγκράτι', 'alex@hotmail.com')");
            PreparedStatement addPerson4 = dBconnection.prepareStatement("INSERT INTO APP.PERSON (NAME, ADDRESS, MAIL) VALUES ('Ιωάννου Γιώργος', 'Κίου 6, Γλυφάδα', 'ioan@in.gr')");
            PreparedStatement addPerson5 = dBconnection.prepareStatement("INSERT INTO APP.PERSON (NAME, ADDRESS, MAIL) VALUES ('Παναγιώτου Δημήτρης', 'Αλεξάνδρου 32, Πάτρα', 'jim@gmail.gr')");
            PreparedStatement addPerson6 = dBconnection.prepareStatement("INSERT INTO APP.PERSON (NAME, ADDRESS, MAIL) VALUES ('Καραγιάννη Μαρία', 'Βενιζέλου 22, Ιωάννινα', 'karag@gmail.gr')");
            addPerson1.executeUpdate();
            addPerson2.executeUpdate();
            addPerson3.executeUpdate();
            addPerson4.executeUpdate();
            addPerson5.executeUpdate();
            addPerson6.executeUpdate();
        } catch (SQLException ex) {
            Logger.getLogger(DBManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}